﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Org.BouncyCastle.Asn1;
using System.IO;

namespace TheClerk.Forms.Templates.raw.Envelope
{
    // Serialize() complete
    /// <summary>
    /// Everything needed to understand and reference one Assertion
    /// </summary>
    /// <remarks>
    /// An Assertion has one and only one Stream associated with it.
    /// It must have one and only one AssertionType.
    /// It has one (optional) name.
    /// It has one (optional) internal identifier.
    /// 
    /// Assertion ::= SEQUENCE {
    ///	    assertionInternalID INTEGER OPTIONAL,
    ///     assertionName UTF8String OPTIONAL,
    ///     assertionType [7] AssertionType,
    ///     assertionContent OCTET STRING }
    /// </remarks>
    public class Assertion
    {
        #region internal members
        int? assertionInternalID = null;
        string assertionName = null;
        AssertionType assertionType = new AssertionType();
        System.IO.Stream assertionContent = new System.IO.MemoryStream();
        private Stream stream;
        private MemoryStream memoryStream;
        #endregion

        #region main constructor
        public Assertion(Stream input, byte[] input2, AssertionType type,
            string AssertionName, int? AssertionInternalID)
        {
            if (input == null && input2 != null)
                assertionContent = new MemoryStream(input2);
            else if (input != null)
                assertionContent = input;
            else
                assertionContent = new MemoryStream();
            this.assertionInternalID = AssertionInternalID;
            assertionName = AssertionName;
            if (type == null)
                assertionType = new AssertionType(type: "application/octet-string");
            else
                assertionType = type;
        }
        #endregion

        #region Convenience constructors
        public Assertion() : this(null, null, null, null, null) { }

        public Assertion(Stream stream, AssertionType assertionType) :
            this(stream, null, assertionType, null, null) { }

        public Assertion(MemoryStream memoryStream) : this(memoryStream, null, null, null, null) { }
        #endregion

        #region SetType (mandatory)
        /// <summary>
        /// Set the type of the Assertion to a MIME-type or string-form OID
        /// </summary>
        /// <param name="mimeType">MIME-type or string-form OID indicating assertionContent's type</param>
        public void SetType(AssertionType kind)
        {
            assertionType = kind;
        }
        #endregion

        #region SetContent (mandatory)
        public void SetContent(Stream input)
        {
            assertionContent = new MemoryStream();
            input.CopyTo(assertionContent);
        }
        #endregion

        #region SetName (optional)
        /// <summary>
        /// Set name of the Assertion
        /// </summary>
        /// <param name="assertionName">the logical filename of the assertionContent</param>
        /// <remarks>
        /// This causes Assertion to lose reference to its old name.
        /// </remarks>
        public void SetName(string assertionName)
        {
            this.assertionName = assertionName;
        }
        #endregion

        #region Serialize() and ToAsn1Encodable()
        internal System.IO.Stream Serialize()
        {
            // verify we have a type and a content
            if (assertionType == null || assertionContent == null)
                throw new InvalidOperationException("require assertionType and assertionContent");

            // Generate the DER sequence
            var outstream = new System.IO.MemoryStream();
            var sequence = new Org.BouncyCastle.Asn1.DerSequenceGenerator(outstream);
            if (assertionInternalID != null)
                sequence.AddObject(new DerInteger((int)assertionInternalID));
            if (assertionName != null)
                sequence.AddObject(new DerUtf8String(assertionName));
            sequence.AddObject(
                new DerTaggedObject(7, assertionType.ToAsn1Encodable()));
            sequence.AddObject(new DerOctetString(assertionContent.ToByteArray()));
            sequence.Close();
            // Rewind the Stream
            outstream.Position = 0;
            return outstream;
        }

        virtual public Asn1Encodable ToAsn1Encodable()
        {
            return Asn1Sequence.FromStream(Serialize());
        }
        #endregion
    }
}
